#include "interface.h"

#include <QDataStream>
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include "seckeyshm.h"
#include "aescrypto.h"

Interface::Interface(std::string path)
{
    // 解析配置文件, 得到关联共享内存的key
    QFile file(QString::fromStdString(path));
    file.open(QFile::ReadOnly);
    QByteArray json = file.readAll();
    file.close();

    // 解析json字符串, 得到原始数据
    QJsonDocument doc = QJsonDocument::fromJson(json);
    QJsonObject obj = doc.object();
    // {"ServerID":"xx", "ClientID", "xx", "ShmKey":"xx"}
    m_clientID = obj.value("ClientID").toString();
    m_serverID = obj.value("ServerID").toString();
    QString shmkey = obj.value("ShmKey").toString();

    // 关联共享内存, 读出秘钥
    m_shm = new SeckeyShm(shmkey);
}

// 只有发送数据的时候才需要加密
// 根据之前的分析, 肯定是客户端先发送数据
// 那么客户端在发送数据的时候一并将自己的ID发送给服务器, 这样服务器就能
// 根据这个ID找到一个正确的秘钥节点, 用于通信数据的加密/解密
std::string Interface::encryptString(std::string msg)
{
    // 读共享内存
    SeckeyInfo keyinfo = m_shm->readShm(m_clientID, m_serverID);
    // 基于得到的秘钥创建AES对象
    AesCrypto aes(keyinfo.seckey);
    // 加密数据
    QString encMsg = aes.aesCBCEncrypt(QString::fromStdString(msg));
    // 将客户端的ID添加到即将要发送的数据块中: clientid+encMsg
    QByteArray buffer;
    // 二进制流对象封装了一块内存 -> buffer, 通过流对象间接读写buffer
    QDataStream stream(&buffer, QIODevice::ReadWrite);
    // 写入数据
    stream << m_clientID << encMsg;
    // 得到了新的二进制数据块, 数据存储在buffer中, 数据要中转因此进制编码
    buffer = buffer.toBase64();
    // 返回处理好的数据
    return std::string(buffer.data(), buffer.size());
}

// 加密之后的数据发送到接收端, 接收端需要解密
// 第一条数据可定是被服务器接收的, 而不是客户端
std::string Interface::decryptString(std::string msg)
{
    // base64解码
    // QByteArray data = QByteArray::fromBase64(QByteArray(msg.data(), msg.size()));
    QByteArray data = QString::fromStdString(msg).toUtf8();
    data = QByteArray::fromBase64(data);

    // 将data中的二进制数据拆分, 按照存储的顺序依次取出即可
    QString cliid, encMsg;
    QDataStream stream(data);
    stream >> cliid >> encMsg;
    // 如果是服务器端需要使用客户端发送过来的clientID
    m_clientID = cliid;

    // 读共享内存
    SeckeyInfo keyinfo = m_shm->readShm(m_clientID, m_serverID);
    AesCrypto aes(keyinfo.seckey);
    // 数据解密
    encMsg = aes.aesCBCDecrypt(encMsg);

    return encMsg.toStdString();
}
